﻿using System;
using System.Diagnostics;
using System.Resources;
using System.Windows;
using System.Windows.Markup;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using Puddy.Resources;
using Puddy.ViewModels;
using Puddy.Models;
using Puddy.Utils;
using Microsoft.Phone.BackgroundAudio;
using Windows.Devices.Geolocation;
using System.Windows.Threading;
using System.Collections.Generic;
using System.Device.Location;
using System.Threading.Tasks;


namespace Puddy
{
    public partial class App : Application
    {
        public static DispatcherTimer TimerGetMyLocation;

        public static Geolocator Geolocator { get; set; }
        public static GeoCoordinate myCurrentLocation;
        public static PositionStatus currentPositionStatus = PositionStatus.NotInitialized;

        public static bool RunningInBackground { get; set; }
        public static bool RunningAlarm { get; set; }
        public static bool ShouldShowMessTurnOffLocation = false;
        public static String AddressAlarm { get; set; }
        public static String NameAlarm { get; set; }

        public static PhoneApplicationPage MainPage;



        public string AccessToken = "";
        public string UserName = "";
        public string UserImage = "";
        public string UserEmail = "";
        public string UserGender = "";

        private static MainViewModel viewModel = null;

        /// <summary>
        /// A static ViewModel used by the views to bind against.
        /// </summary>
        /// <returns>The MainViewModel object.</returns>
        public static MainViewModel ViewModel
        {
            get
            {
                // Delay creation of the view model until necessary
                if (viewModel == null)
                    viewModel = new MainViewModel();

                return viewModel;
            }
        }

        static RingToneViewModel currentRingTone;

        public static RingToneViewModel CurrentRingTone
        {
            get { return currentRingTone; }
            set { currentRingTone = value; }
        }

        static SettingAlarm setting;

        public static SettingAlarm Setting
        {
            get { return setting; }
            set { setting = value; }
        }

        

        /// <summary>
        /// Provides easy access to the root frame of the Phone Application.
        /// </summary>
        /// <returns>The root frame of the Phone Application.</returns>
        public static PhoneApplicationFrame RootFrame { get; private set; }

        /// <summary>
        /// Constructor for the Application object.
        /// </summary>
        public App()
        {
            // Global handler for uncaught exceptions.
            UnhandledException += Application_UnhandledException;

            // Standard XAML initialization
            InitializeComponent();

            // Phone-specific initialization
            InitializePhoneApplication();

            // Language display initialization
            InitializeLanguage();

            

            // Show graphics profiling information while debugging.
            if (Debugger.IsAttached)
            {
                //BackgroundAudioPlayer.Instance.Close();

                // Display the current frame rate counters.
                Application.Current.Host.Settings.EnableFrameRateCounter = true;

                // Show the areas of the app that are being redrawn in each frame.
                //Application.Current.Host.Settings.EnableRedrawRegions = true;

                // Enable non-production analysis visualization mode,
                // which shows areas of a page that are handed off GPU with a colored overlay.
                //Application.Current.Host.Settings.EnableCacheVisualization = true;

                // Prevent the screen from turning off while under the debugger by disabling
                // the application's idle detection.
                // Caution:- Use this under debug mode only. Application that disables user idle detection will continue to run
                // and consume battery power when the user is not using the phone.
                PhoneApplicationService.Current.UserIdleDetectionMode = IdleDetectionMode.Disabled;
            }
        }

        // Code to execute when the application is launching (eg, from Start)
        // This code will not execute when the application is reactivated
        private void Application_Launching(object sender, LaunchingEventArgs e)
        {
        }

        // Code to execute when the application is activated (brought to foreground)
        // This code will not execute when the application is first launched
        private void Application_Activated(object sender, ActivatedEventArgs e)
        {
            //// Ensure that application state is restored appropriately
            RunningInBackground = false;
           
        }

        private void Application_RunningInBackground(object sender, RunningInBackgroundEventArgs args)
        {
            RunningInBackground = true;
            // Suspend all unnecessary processing such as UI updates
        }


        // Code to execute when the application is deactivated (sent to background)
        // This code will not execute when the application is closing
        private void Application_Deactivated(object sender, DeactivatedEventArgs e)
        {
            // Ensure that required application state is persisted here.
            
        }

        // Code to execute when the application is closing (eg, user hit Back)
        // This code will not execute when the application is deactivated
        private   void Application_Closing(object sender, ClosingEventArgs e)
        {
            //await RestoreData.CreateJsonSetting(App.Setting);
            //await RestoreData.CreateJsonLocationList(App.ViewModel.LocationList);
        }

        // Code to execute if a navigation fails
        private void RootFrame_NavigationFailed(object sender, NavigationFailedEventArgs e)
        {
            if (Debugger.IsAttached)
            {
                // A navigation has failed; break into the debugger
                Debugger.Break();
            }
        }

        // Code to execute on Unhandled Exceptions
        private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
        {
            if (Debugger.IsAttached)
            {
                // An unhandled exception has occurred; break into the debugger
                Debugger.Break();
            }
        }

        #region Phone application initialization

        // Avoid double-initialization
        private bool phoneApplicationInitialized = false;

        // Do not add any additional code to this method
        private void InitializePhoneApplication()
        {
            if (phoneApplicationInitialized)
                return;

            // Create the frame but don't set it as RootVisual yet; this allows the splash
            // screen to remain active until the application is ready to render.
            RootFrame = new TransitionFrame();
            //RootFrame = new PhoneApplicationFrame();
            RootFrame.Navigated += CompleteInitializePhoneApplication;

            // Handle navigation failures
            RootFrame.NavigationFailed += RootFrame_NavigationFailed;

            // Handle reset requests for clearing the backstack
            RootFrame.Navigated += CheckForResetNavigation;

            // Ensure we don't initialize again
            phoneApplicationInitialized = true;
        }

        // Do not add any additional code to this method
        private void CompleteInitializePhoneApplication(object sender, NavigationEventArgs e)
        {
            // Set the root visual to allow the application to render
            if (RootVisual != RootFrame)
                RootVisual = RootFrame;

            // Remove this handler since it is no longer needed
            RootFrame.Navigated -= CompleteInitializePhoneApplication;
        }

        private void CheckForResetNavigation(object sender, NavigationEventArgs e)
        {
            // If the app has received a 'reset' navigation, then we need to check
            // on the next navigation to see if the page stack should be reset
            if (e.NavigationMode == NavigationMode.Reset)
                RootFrame.Navigated += ClearBackStackAfterReset;
        }

        private void ClearBackStackAfterReset(object sender, NavigationEventArgs e)
        {
            // Unregister the event so it doesn't get called again
            RootFrame.Navigated -= ClearBackStackAfterReset;

            // Only clear the stack for 'new' (forward) and 'refresh' navigations
            if (e.NavigationMode != NavigationMode.New && e.NavigationMode != NavigationMode.Refresh)
                return;

            // For UI consistency, clear the entire page stack
            while (RootFrame.RemoveBackEntry() != null)
            {
                ; // do nothing
            }
        }

        #endregion

        // Initialize the app's font and flow direction as defined in its localized resource strings.
        //
        // To ensure that the font of your application is aligned with its supported languages and that the
        // FlowDirection for each of those languages follows its traditional direction, ResourceLanguage
        // and ResourceFlowDirection should be initialized in each resx file to match these values with that
        // file's culture. For example:
        //
        // AppResources.es-ES.resx
        //    ResourceLanguage's value should be "es-ES"
        //    ResourceFlowDirection's value should be "LeftToRight"
        //
        // AppResources.ar-SA.resx
        //     ResourceLanguage's value should be "ar-SA"
        //     ResourceFlowDirection's value should be "RightToLeft"
        //
        // For more info on localizing Windows Phone apps see http://go.microsoft.com/fwlink/?LinkId=262072.
        //
        private void InitializeLanguage()
        {
            try
            {
                // Set the font to match the display language defined by the
                // ResourceLanguage resource string for each supported language.
                //
                // Fall back to the font of the neutral language if the Display
                // language of the phone is not supported.
                //
                // If a compiler error is hit then ResourceLanguage is missing from
                // the resource file.
                RootFrame.Language = XmlLanguage.GetLanguage(AppResources.ResourceLanguage);

                // Set the FlowDirection of all elements under the root frame based
                // on the ResourceFlowDirection resource string for each
                // supported language.
                //
                // If a compiler error is hit then ResourceFlowDirection is missing from
                // the resource file.
                FlowDirection flow = (FlowDirection)Enum.Parse(typeof(FlowDirection), AppResources.ResourceFlowDirection);
                RootFrame.FlowDirection = flow;

                
            }
            catch
            {
                // If an exception is caught here it is most likely due to either
                // ResourceLangauge not being correctly set to a supported language
                // code or ResourceFlowDirection is set to a value other than LeftToRight
                // or RightToLeft.

                if (Debugger.IsAttached)
                {
                    Debugger.Break();
                }

                throw;
            }
        }

        

        //private static int getTimeToGetLocation()
        //{
        //    //default 60s
        //    return 60;
        //}

        /// <summary>
        /// MovementThreshold = distance /2 : do it simple :D
        ///
        /// </summary>
        /// <param name="myLocation"></param>
        /// <returns> distance geolocator update location </returns>
        //private static double getDistanceLocatorUpdateLocaton(GeoCoordinate myLocation)
        //{
        //    // min distance from user to alarm location
        //    double minDistance = 0;

        //    foreach (LocationViewModel location in App.ViewModel.ListItemStateOn)
        //    {
        //        if (!location.AlreadyAlarm)
        //        {
        //            var l = new GeoCoordinate(location.Latitude, location.Longitude);
        //            double distance = myLocation.GetDistanceTo(l);

        //            if (distance < minDistance)
        //                minDistance = distance; 
        //        }
                               
        //    }

        //    minDistance = minDistance - App.Setting.DistanceByMeter;

        //    return minDistance / 2;
        //}

        public static bool checkNetworkAvailble()
        {
            return (Microsoft.Phone.Net.NetworkInformation.NetworkInterface.NetworkInterfaceType != Microsoft.Phone.Net.NetworkInformation.NetworkInterfaceType.None);
        }

        private static void setLevelPositionAccuracy()
        {
            //App.Geolocator.PositionChanged -= geolocator_PositionChanged;
            //App.Geolocator.StatusChanged   -= Geolocator_StatusChanged;

            //// min distance from user to alarm location
            //double minDistance = 0;

            //foreach (LocationViewModel location in App.ViewModel.ListItemStateOn)
            //{
            //    if (!location.AlreadyAlarm)
            //    {
            //        var l = new GeoCoordinate(location.Latitude, location.Longitude);
            //        double distance = App.myCurrentLocation.GetDistanceTo(l);

            //        if (distance < minDistance)
            //            minDistance = distance;
            //    }

            //}

            //// if min distance > distance to alarm in setting + 1000m -> set get location state in low level and vice versa in high level
            //if (minDistance > App.Setting.DistanceByMeter + 1000)
            //{
            //    if (App.checkNetworkAvailble())
            //        App.Geolocator.DesiredAccuracy = PositionAccuracy.Default;
            //    else
            //        App.Geolocator.DesiredAccuracy = PositionAccuracy.High;
            //}
            //else
            //{
            //    App.Geolocator.DesiredAccuracy = PositionAccuracy.High;
            //}

            //App.Geolocator.PositionChanged += geolocator_PositionChanged;
            //App.Geolocator.StatusChanged += Geolocator_StatusChanged;

        }

        public static async Task getCurrentPosition()
        {
            try
            {
                Geoposition geoposition = await App.Geolocator.GetGeopositionAsync(
                                maximumAge: TimeSpan.FromMinutes(5),
                                timeout: TimeSpan.FromSeconds(10)
                                );

                App.myCurrentLocation = geoposition.Coordinate.ToGeoCoordinate();
            }
            catch (Exception ex)
            {
                if ((uint)ex.HResult == 0x80004004)
                {
                    // the application does not have the right capability or the location master switch is off
                    if (!App.RunningInBackground)
                        MessageBox.Show(Constants.MES_TURN_OFF_LOCATION);
                }
                //else
                {
                    // something else happened acquring the location
                }
            }
        }

        public static async void getCurrentPositionAsync()
        {
            try
            {
                Geoposition geoposition = await App.Geolocator.GetGeopositionAsync(
                                maximumAge: TimeSpan.FromMinutes(5),
                                timeout: TimeSpan.FromSeconds(10)
                                );

                App.myCurrentLocation = geoposition.Coordinate.ToGeoCoordinate();
            }
            catch (Exception ex)
            {
                if ((uint)ex.HResult == 0x80004004)
                {
                    // the application does not have the right capability or the location master switch is off
                    if (!App.RunningInBackground)
                        MessageBox.Show(Constants.MES_TURN_OFF_LOCATION);
                }
                //else
                {
                    // something else happened acquring the location
                }
            }
        }

        public static void initLocation()
        {
            if (App.Geolocator == null)
            {
                App.Geolocator = new Geolocator();

                //if (checkNetworkAvailble())
                //    App.Geolocator.DesiredAccuracy = PositionAccuracy.Default;
                //else
                    App.Geolocator.DesiredAccuracy = PositionAccuracy.High;


                App.Geolocator.MovementThreshold = 100; // The units are meters.
                
                App.Geolocator.StatusChanged += Geolocator_StatusChanged;
            }
        }

        public static  void turnOnTrackLocation()
        {
            if (App.Geolocator == null)
            {
                App.Geolocator = new Geolocator();

                //if (checkNetworkAvailble())
                //    App.Geolocator.DesiredAccuracy = PositionAccuracy.Default;
                //else
                    App.Geolocator.DesiredAccuracy = PositionAccuracy.High;


                App.Geolocator.MovementThreshold = 100; // The units are meters.

                App.Geolocator.StatusChanged += Geolocator_StatusChanged;
            }


            App.Geolocator.PositionChanged -= geolocator_PositionChanged;
            App.Geolocator.PositionChanged += geolocator_PositionChanged;
            
            

            

            //if (App.TimerGetMyLocation == null)
            //{
            //    TimerGetMyLocation = new DispatcherTimer();
            //    TimerGetMyLocation.Tick += timerGetMyLocation_Tick;
            //    int timeToUpdate = getTimeToGetLocation();
            //    TimerGetMyLocation.Interval = new TimeSpan(0, 0, timeToUpdate);
            //}

            //if (!TimerGetMyLocation.IsEnabled)
            //    TimerGetMyLocation.Start();

            //Debug.WriteLine("TimerGetMyLocation.Start");
        }

        
        
        public static void turnOffTrackLocation()
        {
            if (App.Geolocator != null)
            {
                App.Geolocator.PositionChanged -= geolocator_PositionChanged;
                App.Geolocator.StatusChanged -= Geolocator_StatusChanged;
                App.Geolocator = null;
            }

            //dispatcher timer not use in background task
            //TimerGetMyLocation.Stop();
        }

        

        public static async void recheckLocation()
        {
            
            if (App.myCurrentLocation == null) 
            {
                await App.getCurrentPosition();
            }

            String addressAlarm = checkUserToAlarmedPlace(App.myCurrentLocation.Latitude, App.myCurrentLocation.Longitude);

            if (addressAlarm != null)
            {
                handleWhenUserComing(addressAlarm);
            }

            setLevelPositionAccuracy();
            
        }

        public static void geolocator_PositionChanged(Geolocator sender, PositionChangedEventArgs args)
        {
            Debug.WriteLine("geolocator_PositionChanged");
            App.myCurrentLocation = args.Position.Coordinate.ToGeoCoordinate();
            String addressAlarm = checkUserToAlarmedPlace(args.Position.Coordinate.Latitude, args.Position.Coordinate.Longitude);

            if (addressAlarm != null)
            {
                handleWhenUserComing(addressAlarm);
            }

            setLevelPositionAccuracy();
        }

        static void Geolocator_StatusChanged(Geolocator sender, StatusChangedEventArgs args)
        {
            Debug.WriteLine("Geolocator_StatusChanged");

            switch (args.Status)
            {
                case PositionStatus.Disabled:
                    // the application does not have the right capability or the location master switch is off
                    //status = "location is disabled in phone settings";
                    if (!App.RunningInBackground)
                    {
                        Deployment.Current.Dispatcher.BeginInvoke(() =>
                        {
                            MessageBox.Show(Constants.MES_TURN_OFF_LOCATION);
                        });

                    }
                    else
                    {
                        Microsoft.Phone.Shell.ShellToast toast = new Microsoft.Phone.Shell.ShellToast();
                        toast.Content = Constants.MES_TURN_OFF_LOCATION_FOR_TOAST;
                        toast.Title = "Warning:  ";
                        toast.Show();

                        if (currentPositionStatus == PositionStatus.Ready)
                        {
                            ShouldShowMessTurnOffLocation = true;
                        }
                    }

                    

                    currentPositionStatus = PositionStatus.Disabled;
                    break;
                case PositionStatus.Initializing:
                    // the geolocator started the tracking operation
                    //status = "initializing";
                    
                    break;
                case PositionStatus.NoData:
                    // the location service was not able to acquire the location
                    if (!App.RunningInBackground)
                    {
                        Deployment.Current.Dispatcher.BeginInvoke(() =>
                        {
                            MessageBox.Show("Could not get your location!");
                        });

                    }
                    break;
                case PositionStatus.Ready:
                    // the location service is generating geopositions as specified by the tracking parameters
                    //status = "ready";
                    if (currentPositionStatus == PositionStatus.NotInitialized)
                    {
                        App.getCurrentPositionAsync();
                    }
                    else if (currentPositionStatus == PositionStatus.Disabled)
                    {
                        
                    }

                    currentPositionStatus = PositionStatus.Ready;
                    break;
                case PositionStatus.NotAvailable:
                    //status = "not available";
                    // not used in WindowsPhone, Windows desktop uses this value to signal that there is no hardware capable to acquire location information
                    break;
                case PositionStatus.NotInitialized:
                    // the initial state of the geolocator, once the tracking operation is stopped by the user the geolocator moves back to this state

                    break;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns> return place</returns>
        static String checkUserToAlarmedPlace(double lat, double lng)
        {
            var myLocation = new GeoCoordinate(lat, lng);

            String addressAlarm = null;

            foreach (LocationViewModel location in App.ViewModel.ListItemStateOn)
            {
                var l = new GeoCoordinate(location.Latitude, location.Longitude);
                double distance = myLocation.GetDistanceTo(l);

                if (distance < App.setting.DistanceByMeter)
                {
                    if (!location.AlreadyAlarm)
                    {
                        location.AlreadyAlarm = true;
                        addressAlarm = location.Address;
                        App.NameAlarm = location.Name;
                        break;
                    }
                }
                else
                {
                    location.AlreadyAlarm = false;
                }
            }

            return addressAlarm;
        }

        private static void handleWhenUserComing(String addressAlarm)
        {
            App.RunningAlarm = true;
            App.AddressAlarm = addressAlarm;

            if (!App.RunningInBackground)
            {
                Deployment.Current.Dispatcher.BeginInvoke(() =>
                {
                    Debug.WriteLine("MainPage. NavigationService.Navigate -> alarm page");
                    MainPage. NavigationService.Navigate(new Uri("/AlarmPage.xaml", UriKind.Relative));
                });
                
            }
            else
            {
                Microsoft.Phone.Shell.ShellToast toast = new Microsoft.Phone.Shell.ShellToast();
                toast.Content = addressAlarm;
                toast.Title = "Alarm:  ";
                toast.NavigationUri = new Uri("/AlarmPage.xaml?IsFromBG=" + "true", UriKind.Relative);
                toast.Show();

            }


            AudioTrack audio = new AudioTrack(new Uri(App.CurrentRingTone.Name.Replace(" ", "") + ".mp3", UriKind.Relative),
                         App.CurrentRingTone.Name,
                        "Windows Phone",
                        "",
                        null);

            BackgroundAudioPlayer.Instance.Track = audio;
            BackgroundAudioPlayer.Instance.Play();

            Debug.WriteLine("App.RunningAlarm = true");
            App.RunningAlarm = true;
            App.AddressAlarm = addressAlarm;
        }

        //static async void timerGetMyLocation_Tick(object sender, EventArgs e)
        //{
        //    Debug.WriteLine("timerGetMyLocation_Tick");
        //    GeoCoordinate myLocation = null;
        //    try
        //    {
        //        Geoposition geoposition = await App.Geolocator.GetGeopositionAsync(
        //            maximumAge: TimeSpan.FromMinutes(5),
        //            timeout: TimeSpan.FromSeconds(10)
        //            );

        //        myLocation = geoposition.Coordinate.ToGeoCoordinate();

        //    }
        //    catch (Exception ex)
        //    {
        //        if ((uint)ex.HResult == 0x80004004)
        //        {
        //            // the application does not have the right capability or the location master switch is off
        //            MessageBox.Show("Your location is currently turned off. To change your location setting, open the settings charm and tap permissions. Then click again to start using location again.");
        //            //toast.Show("GPS is disabled, this may cause the Puddy app to fail.");
        //        }
        //        //else
        //        {
        //            // something else happened acquring the location
        //        }
        //    }

        //    if (myLocation != null)
        //    {
        //        String addressAlarm = null;
        //        foreach (LocationViewModel location in App.ViewModel.ListItemStateOn)
        //        {
        //            var l = new GeoCoordinate(location.Latitude, location.Longitude);
        //            double distance = myLocation.GetDistanceTo(l);

        //            if (distance < App.setting.DistanceByMeter)
        //            {
        //                //if this location has just alarm -> dont alarm any more
        //                //if (!location.AlreadyAlarm)
        //                //{
        //                    location.AlreadyAlarm = true;
        //                    addressAlarm = location.Address;
        //                    break;
        //                //}

        //            }
        //            else
        //            {
        //                location.AlreadyAlarm = false;
        //            }
        //        }

        //        if (addressAlarm != null)
        //        {

        //            if (!App.RunningInBackground)
        //            {
        //                //Dispatcher.BeginInvoke(() =>
        //                //{
        //                //    //

        //                //});
        //                MainPage.NavigationService.Navigate(new Uri("/AlarmPage.xaml", UriKind.Relative));
        //            }
        //            else
        //            {
        //                Microsoft.Phone.Shell.ShellToast toast = new Microsoft.Phone.Shell.ShellToast();
        //                toast.Content = addressAlarm;
        //                toast.Title = "Alarm:  ";
        //                toast.NavigationUri = new Uri("/AlarmPage.xaml?IsFromBG=" + "true", UriKind.Relative);
        //                toast.Show();

        //            }


        //            AudioTrack audio = new AudioTrack(new Uri(App.CurrentRingTone.Name.Replace(" ", "") + ".mp3", UriKind.Relative),
        //                         App.CurrentRingTone.Name,
        //                        "Windows Phone",
        //                        "",
        //                        null);

        //            BackgroundAudioPlayer.Instance.Track = audio;
        //            BackgroundAudioPlayer.Instance.Play();

        //            Debug.WriteLine("App.RunningAlarm = true");
        //            App.RunningAlarm = true;
        //            App.AddressAlarm = addressAlarm;
        //        }

        //    }

        //    //update again interval
        //    int timeToUpdate = getTimeToGetLocation();
        //    TimerGetMyLocation.Interval = new TimeSpan(0, 0, timeToUpdate);
        //}

        


        
    }
}